package org.dromara.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.constant.UserConstants;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.TreeBuildUtils;
import org.dromara.common.mybatis.helper.DataBaseHelper;
import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.SysDeptBo;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.mapper.*;
import org.dromara.system.service.ISysDeptService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 部门管理 服务实现
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Service
public class SysDeptServiceImpl implements ISysDeptService {

    private final SysDeptMapper baseMapper;
    private final SysRoleMapper roleMapper;
    private final SysUserMapper userMapper;

    /**
     * 查询部门管理数据
     *
     * @param dept 部门信息
     * @return 部门信息集合
     */
    @Override
    public List<SysDeptVo> selectDeptList(SysDeptBo dept) {
        LambdaQueryWrapper<SysDept> lqw = buildQueryWrapper(dept);
        return baseMapper.selectDeptList(lqw);
    }

    /**
     * 查询部门树结构信息
     *
     * @param bo 部门信息
     * @return 部门树信息集合
     */
    @Override
    public List<Tree<Long>> selectDeptTreeList(SysDeptBo bo) {
        // 只查询未禁用部门
        bo.setStatus(UserConstants.DEPT_NORMAL);
        LambdaQueryWrapper<SysDept> lqw = buildQueryWrapper(bo);
        List<SysDeptVo> depts = baseMapper.selectDeptList(lqw);
        buildDeptData(depts);
        return buildDeptTreeSelect(depts);
    }

    private void buildDeptData(List<SysDeptVo> depts) {
        if (CollectionUtils.isEmpty(depts)) {
            return;
        }
        /**
         * 部门id的集合
         */
        List<Long> deptIds = depts.stream().map(SysDeptVo::getDeptId).distinct().collect(Collectors.toList());
        //查询部门用户
        List<SysUserDept> userDepts = sysUserDeptMapper.selectList(
                Wrappers.lambdaQuery(SysUserDept.class).in(SysUserDept::getDeptId, deptIds));

        Map<Long, List<Long>> deptUserIdsMap = userDepts.stream()
                .collect(Collectors.groupingBy(
                        SysUserDept::getDeptId,
                        Collectors.mapping(
                                SysUserDept::getUserId,
                                Collectors.toList()
                        )
                ));
        //查询部门门店
        List<SysDeptStore> deptStores = sysDeptStoreMapper.selectList(
                Wrappers.lambdaQuery(SysDeptStore.class).in(SysDeptStore::getDeptId, deptIds));

        Map<Long, List<Long>> deptStoreIdsMap = deptStores.stream()
                .collect(Collectors.groupingBy(
                        SysDeptStore::getDeptId,
                        Collectors.mapping(
                                SysDeptStore::getStoreId,
                                Collectors.toList()
                        )
                ));
        //查询部门区域
        List<SysDeptArea> deptAreas = sysDeptAreaMapper.selectList(
                Wrappers.lambdaQuery(SysDeptArea.class).in(SysDeptArea::getDeptId, deptIds));
        Map<Long, List<Long>> deptAreaIdsMap = deptAreas.stream()
                .collect(Collectors.groupingBy(
                        SysDeptArea::getDeptId,
                        Collectors.mapping(
                                SysDeptArea::getAreaId,
                                Collectors.toList()
                        )
                ));
        depts.forEach(vo -> {
            vo.setStoreIds(deptStoreIdsMap.get(vo.getDeptId()));
            vo.setAreaIds(deptAreaIdsMap.get(vo.getDeptId()));
            vo.setUserIds(deptUserIdsMap.get(vo.getDeptId()));
        });

    }

    private LambdaQueryWrapper<SysDept> buildQueryWrapper(SysDeptBo bo) {
        LambdaQueryWrapper<SysDept> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysDept::getDelFlag, "0");
        lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId());
        lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId());
        lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus());
        lqw.orderByAsc(SysDept::getParentId);
        lqw.orderByAsc(SysDept::getOrderNum);
        lqw.orderByAsc(SysDept::getDeptId);
        return lqw;
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param depts 部门列表
     * @return 下拉树结构列表
     */
    @Override
    public List<Tree<Long>> buildDeptTreeSelect(List<SysDeptVo> depts) {
        if (CollUtil.isEmpty(depts)) {
            return CollUtil.newArrayList();
        }
        return TreeBuildUtils.build(depts, (area, node) -> {
            node.setId(area.getDeptId());
            node.setParentId(area.getParentId());
            node.setWeight(area.getOrderNum());    //排序权重
            node.setName(area.getDeptName());
            // 如果不想一个个putExtra，可以直接Bean 转 Map 遍历赋值
            Map<String, Object> map = BeanUtil.beanToMap(area);
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                node.putExtra(entry.getKey(), entry.getValue());
            }
        });

    }

    /**
     * 根据角色ID查询部门树信息
     *
     * @param roleId 角色ID
     * @return 选中部门列表
     */
    @Override
    public List<Long> selectDeptListByRoleId(Long roleId) {
        SysRole role = roleMapper.selectById(roleId);
        return baseMapper.selectDeptListByRoleId(roleId, role.getDeptCheckStrictly());
    }

    /**
     * 根据部门ID查询信息
     *
     * @param deptId 部门ID
     * @return 部门信息
     */
    @Cacheable(cacheNames = CacheNames.SYS_DEPT, key = "#deptId")
    @Override
    public SysDeptVo selectDeptById(Long deptId) {
        SysDeptVo dept = baseMapper.selectVoById(deptId);
        if (ObjectUtil.isNull(dept)) {
            return null;
        }
        SysDeptVo parentDept = baseMapper.selectVoOne(new LambdaQueryWrapper<SysDept>()
                .select(SysDept::getDeptName).eq(SysDept::getDeptId, dept.getParentId()));
        dept.setParentName(ObjectUtil.isNotNull(parentDept) ? parentDept.getDeptName() : null);
        buildDeptData(CollUtil.newArrayList(dept));
        return dept;
    }

    /**
     * 通过部门ID查询部门名称
     *
     * @param deptIds 部门ID串逗号分隔
     * @return 部门名称串逗号分隔
     */
    public String selectDeptNameByIds(String deptIds) {
        List<String> list = new ArrayList<>();
        for (Long id : StringUtils.splitTo(deptIds, Convert::toLong)) {
            SysDeptVo vo = SpringUtils.getAopProxy(this).selectDeptById(id);
            if (ObjectUtil.isNotNull(vo)) {
                list.add(vo.getDeptName());
            }
        }
        return String.join(StringUtils.SEPARATOR, list);
    }

    /**
     * 根据ID查询所有子部门数（正常状态）
     *
     * @param deptId 部门ID
     * @return 子部门数
     */
    @Override
    public long selectNormalChildrenDeptById(Long deptId) {
        return baseMapper.selectCount(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL)
                .apply(DataBaseHelper.findInSet(deptId, "ancestors")));
    }

    /**
     * 是否存在子节点
     *
     * @param deptId 部门ID
     * @return 结果
     */
    @Override
    public boolean hasChildByDeptId(Long deptId) {
        return baseMapper.exists(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getParentId, deptId));
    }

    /**
     * 查询部门是否存在用户
     *
     * @param deptId 部门ID
     * @return 结果 true 存在 false 不存在
     */
    @Override
    public boolean checkDeptExistUser(Long deptId) {
        return userMapper.exists(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getDeptId, deptId));
    }

    /**
     * 校验部门名称是否唯一
     *
     * @param dept 部门信息
     * @return 结果
     */
    @Override
    public boolean checkDeptNameUnique(SysDeptBo dept) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getDeptName, dept.getDeptName())
                .eq(SysDept::getParentId, dept.getParentId())
                .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId()));
        return !exist;
    }

    /**
     * 校验部门编码是否唯一
     *
     * @param dept 部门信息
     * @return 结果
     */
    @Override
    public boolean checkDeptCodeUnique(SysDeptBo dept) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysDept>()
                .eq(SysDept::getDeptCode, dept.getDeptCode())
                .eq(SysDept::getParentId, dept.getParentId())
                .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId()));
        return !exist;
    }

    /**
     * 校验部门是否有数据权限
     *
     * @param deptId 部门id
     */
    @Override
    public void checkDeptDataScope(Long deptId) {
        if (ObjectUtil.isNull(deptId)) {
            return;
        }
        if (LoginHelper.isSuperAdmin()) {
            return;
        }
        SysDeptVo dept = baseMapper.selectDeptById(deptId);
        if (ObjectUtil.isNull(dept)) {
            throw new ServiceException("没有权限访问部门数据！");
        }
    }

    /**
     * 校验部门是否有数据权限
     *
     * @param deptIds 部门id
     */
    @Override
    public void checkDeptDataScope(List<Long> deptIds) {
        if (CollUtil.isEmpty(deptIds)) {
            return;
        }
        if (LoginHelper.isSuperAdmin()) {
            return;
        }
        List<SysDeptVo> deptVos = baseMapper.selectDeptList(new LambdaQueryWrapper<SysDept>()
                .in(SysDept::getDeptId, deptIds));
        if (CollUtil.isEmpty(deptVos)) {
            throw new ServiceException("没有权限访问部门数据！");
        }
    }

    /**
     * 新增保存部门信息
     *
     * @param bo 部门信息
     * @return 结果
     */
    @Override
    public Boolean insertDept(SysDeptBo bo) {
        SysDept info = baseMapper.selectById(bo.getParentId());
        // 如果父节点不为正常状态,则不允许新增子节点
        if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) {
            throw new ServiceException("部门停用，不允许新增");
        }
        SysDept dept = MapstructUtils.convert(bo, SysDept.class);
        dept.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + dept.getParentId());
        boolean flag = baseMapper.insert(dept) > 0;
        if (flag) {
            bo.setDeptId(dept.getDeptId());
            addDeptInfo(bo);
        }
        return flag;
    }

    private final SysDeptStoreMapper sysDeptStoreMapper;

    private final SysDeptAreaMapper sysDeptAreaMapper;

    private final SysUserDeptMapper sysUserDeptMapper;

    private void addDeptInfo(SysDeptBo bo) {
        //先清除 再添加
        sysDeptAreaMapper.delete(new LambdaQueryWrapper<SysDeptArea>()
                .eq(SysDeptArea::getDeptId, bo.getDeptId()));
        sysUserDeptMapper.delete(new LambdaQueryWrapper<SysUserDept>()
                .eq(SysUserDept::getDeptId, bo.getDeptId()));
        sysDeptStoreMapper.delete(new LambdaQueryWrapper<SysDeptStore>()
                .eq(SysDeptStore::getDeptId, bo.getDeptId()));
        List<Long> areas = bo.getAreaIds();
        if (CollectionUtils.isNotEmpty(areas)) {
            List<SysDeptArea> areaStores = areas.stream().map(area -> {
                SysDeptArea areaStore = new SysDeptArea();
                areaStore.setDeptId(bo.getDeptId());
                areaStore.setAreaId(area);
                return areaStore;
            }).collect(Collectors.toList());
            sysDeptAreaMapper.insertBatch(areaStores);
        }
        List<Long> stores = bo.getStoreIds();
        if (CollectionUtils.isNotEmpty(stores)) {
            List<SysDeptStore> deptStores = stores.stream().map(store -> {
                SysDeptStore areaStore = new SysDeptStore();
                areaStore.setDeptId(bo.getDeptId());
                areaStore.setStoreId(store);
                return areaStore;
            }).collect(Collectors.toList());
            sysDeptStoreMapper.insertBatch(deptStores);
        }
        List<Long> users = bo.getUserIds();
        if (CollectionUtils.isNotEmpty(users)) {
            List<SysUserDept> deptUsers = users.stream().map(user -> {
                SysUserDept areaStore = new SysUserDept();
                areaStore.setDeptId(bo.getDeptId());
                areaStore.setUserId(user);
                return areaStore;
            }).collect(Collectors.toList());
            sysUserDeptMapper.insertBatch(deptUsers);
        }
    }

    /**
     * 修改保存部门信息
     *
     * @param bo 部门信息
     * @return 结果
     */
    @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#bo.deptId")
    @Override
    public Boolean updateDept(SysDeptBo bo) {
        SysDept dept = MapstructUtils.convert(bo, SysDept.class);
        SysDept oldDept = baseMapper.selectById(dept.getDeptId());
        if (!oldDept.getParentId().equals(dept.getParentId())) {
            // 如果是新父部门 则校验是否具有新父部门权限 避免越权
            this.checkDeptDataScope(dept.getParentId());
            SysDept newParentDept = baseMapper.selectById(dept.getParentId());
            if (ObjectUtil.isNotNull(newParentDept) && ObjectUtil.isNotNull(oldDept)) {
                String newAncestors = newParentDept.getAncestors() + StringUtils.SEPARATOR + newParentDept.getDeptId();
                String oldAncestors = oldDept.getAncestors();
                dept.setAncestors(newAncestors);
                updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors);
            }
        }
        int result = baseMapper.updateById(dept);
        if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors())
                && !StringUtils.equals(UserConstants.DEPT_NORMAL, dept.getAncestors())) {
            // 如果该部门是启用状态，则启用该部门的所有上级部门
            updateParentDeptStatusNormal(dept);
        }
        addDeptInfo(bo);
        //TODO 对用户关联的门店先不做，不变更，替换，修改
        return result > 0;
    }

    /**
     * 修改该部门的父级部门状态
     *
     * @param dept 当前部门
     */
    private void updateParentDeptStatusNormal(SysDept dept) {
        String ancestors = dept.getAncestors();
        Long[] deptIds = Convert.toLongArray(ancestors);
        baseMapper.update(null, new LambdaUpdateWrapper<SysDept>()
                .set(SysDept::getStatus, UserConstants.DEPT_NORMAL)
                .in(SysDept::getDeptId, Arrays.asList(deptIds)));
    }

    /**
     * 修改子元素关系
     *
     * @param deptId       被修改的部门ID
     * @param newAncestors 新的父ID集合
     * @param oldAncestors 旧的父ID集合
     */
    private void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) {
        List<SysDept> children = baseMapper.selectList(new LambdaQueryWrapper<SysDept>()
                .apply(DataBaseHelper.findInSet(deptId, "ancestors")));
        List<SysDept> list = new ArrayList<>();
        for (SysDept child : children) {
            SysDept dept = new SysDept();
            dept.setDeptId(child.getDeptId());
            dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
            list.add(dept);
        }
        if (CollUtil.isNotEmpty(list)) {
            if (baseMapper.updateBatchById(list)) {
                list.forEach(dept -> CacheUtils.evict(CacheNames.SYS_DEPT, dept.getDeptId()));
            }
        }
    }

    /**
     * 删除部门管理信息
     *
     * @param deptId 部门ID
     * @return 结果
     */
    @CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#deptId")
    @Override
    public int deleteDeptById(Long deptId) {
        //删除部门门店 和 部门区域. 删除用户。
        sysDeptAreaMapper.delete(new LambdaQueryWrapper<SysDeptArea>()
                .eq(SysDeptArea::getDeptId, deptId));
        sysDeptStoreMapper.delete(new LambdaQueryWrapper<SysDeptStore>()
                .eq(SysDeptStore::getDeptId, deptId));
        sysUserDeptMapper.delete(new LambdaQueryWrapper<SysUserDept>().
                eq(SysUserDept::getDeptId, deptId));
        return baseMapper.deleteById(deptId);
    }

    @Override
    public List<Tree<Long>> selectDeptUserTreeList(SysDeptBo bo) {
        bo.setStatus(UserConstants.DEPT_NORMAL);
        LambdaQueryWrapper<SysDept> lqw = buildQueryWrapper(bo);
        List<SysDeptVo> depts = baseMapper.selectDeptUserList(lqw);
        return buildDeptTreeSelect(depts);
    }

}
